package net.liuyimin.util;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * Zookeeper分布式锁
 */
public class ZkDistributedLock implements Lock {

    private static final String ROOT_PATH = "/distributed";

    private String path;

    private ZooKeeper zooKeeper;

    /**
     * 构造方法
     * @param zooKeeper
     * @param lockName
     */
    public ZkDistributedLock(ZooKeeper zooKeeper, String lockName){
        this.zooKeeper = zooKeeper;
        this.path = ROOT_PATH + "/" + lockName;
    }

    /**
     * 获取锁，如果获取不到，则一直阻塞
     * 获取锁得本质就是创建znode节点过程
     */
    @Override
    public void lock() {
        this.tryLock();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
    }

    /**
     * 尝试获取锁，如果获取不到，则立即返回，不会阻塞
     * @return
     */
    @Override
    public boolean tryLock() {
        try {
            // 创建znode临时节点过程,为了防止zk客户端程序获取锁之后,服务器宕机带来的死锁问题.这是因为客户端宕机，链接失效，临时节点会被删除
            zooKeeper.create(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        } catch (Exception e) {
            // 重试
            try {
                Thread.sleep(200);
                this.lock();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
        return true;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    /**
     * 解锁: 删除node节点的过程
     */
    @Override
    public void unlock() {
        try {
//            this.zooKeeper.delete(path, 0);
            this.zooKeeper.delete(path, -1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Condition newCondition() {
        return null;
    }

}
